Attaching package: 'gm'
The following object is masked from 'package:methods':
show
R
Exploring the intersection of probability and music
Duke Stats
2025-08-09

Plenty of applications from the natural and social sciences:
But what about the arts?
AKA: chance or aleatoric music.


Think of each member of a 46-piece string orchestra as a Brownian particle drifting up and down the staff:

“[E]very member of the orchestra is on the honor system.” Leonard Bernstein
Well …it’s the thought that counts.
Thought: Can I prompt students to use what they know about probability distributions and simulation to write their own pieces of stochastic music?
Worry: Depends. Can you work with music in R?
gm package
“grammar of music”
R with a ggplot2-style interface;“generate music”
MuseScore generates sheet music and MIDI playback.
Let’s transcribe it!
We will add layers to this:
Attaching package: 'gm'
The following object is masked from 'package:methods':
show



"F#5" means the F♯ in the fifth octave on the piano, etc.
(BTW: this plays nice with Quarto off-the-shelf.)
right_hand <- Line(
pitches = list(NA, "E5", "F#5", "D5", "E5", "F#5",
"B5", "G5", "D5",
"E5", "G4",
c("D4", "F#4", "B4"), c("C#4", "F4", "C5"),
c("G3", "C4", "E4"), c("D#4", "G4"), c("C4", "E4")),
durations = c(0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
1, 1, 2,
1, 1,
2, 1,
1.5, 0.5, 1)
)
prelude <- Music() +
Meter(3, 4) +
Tempo(60) +
right_hand
left_hand <- Line(
pitches = c("A2", "E3", "G3", "C4", "E4",
"G4", "C5", NA,
NA, "E2", "E3", "F#3", "G#3",
NA, "A2", "E3", "B3", "G3"),
durations = c(rep(0.5, 7), 2,
rep(0.5, 4), 1,
rep(0.5, 4), 1),
bar = 2, offset = 0.5
)
prelude <- Music() +
Meter(3, 4) +
Tempo(60) +
right_hand +
left_hand +
Clef("F")Note: rests correspond to missing values (NA) in the line.


… and so on
gm packageA ggplot2-style interface for music:
Now, let’s write some crazy music!
pitches <- c("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
octaves <- 1:7
all_pitches <- c("A0", "A#0", "B0",
paste(rep(pitches, length(octaves)),
sort(rep(octaves, length(pitches))),
sep = ""),
"C8")
all_pitches [1] "A0" "A#0" "B0" "C1" "C#1" "D1" "D#1" "E1" "F1" "F#1" "G1" "G#1"
[13] "A1" "A#1" "B1" "C2" "C#2" "D2" "D#2" "E2" "F2" "F#2" "G2" "G#2"
[25] "A2" "A#2" "B2" "C3" "C#3" "D3" "D#3" "E3" "F3" "F#3" "G3" "G#3"
[37] "A3" "A#3" "B3" "C4" "C#4" "D4" "D#4" "E4" "F4" "F#4" "G4" "G#4"
[49] "A4" "A#4" "B4" "C5" "C#5" "D5" "D#5" "E5" "F5" "F#5" "G5" "G#5"
[61] "A5" "A#5" "B5" "C6" "C#6" "D6" "D#6" "E6" "F6" "F#6" "G6" "G#6"
[73] "A6" "A#6" "B6" "C7" "C#7" "D7" "D#7" "E7" "F7" "F#7" "G7" "G#7"
[85] "A7" "A#7" "B7" "C8"
Sample the pitches with replacement:
set.seed(8675309)
line1 <- Line(
pitches = sample(all_pitches, 64, replace = TRUE),
durations = .25
)
line2 <- Line(
pitches = sample(all_pitches, 64, replace = TRUE),
durations = .25
)
kitten <- Music() +
Meter(4, 4) +
Tempo(120) +
line1 +
line2 +
Dynamic("p", 1) +
Dynamic("ffff", 64) +
Hairpin("<", 2, 63)
It’s a time series!

Game:
From Nierhaus’ Algorithmic Composition (2009 Springer):

A first-order Markov chain on the C minor scale is described by its transition probabilities: given the note we’re on right now, what are the probabilities for the next note to come?
Loads of options to explore:

Take an existing piece of music and “add noise.”
So this:
\[ \mathbf{y}=f(\mathbf{x})+\boldsymbol{\varepsilon}, \]
only…it’s music?

(John, please don’t forget to lower the volume on this.)
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
Warning in positions[i] <- new_pos: number of items to replace is not a
multiple of replacement length
gm handle any Hz, and not just the discrete pitches of the Western system?gm package is so much fun to play with!